package org.opens.markdown;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 功能描述
 *
 * @author by zhangyuming
 * @date 2023/1/9
 */
public class MdUtil {

    private static final String DEFAULT_SECOND_PATH = "image";

    private static final String IMAGE_REGEX = "\\!\\[(?<name>.*?)\\]\\((?<path>.*?)\\)";

    private static Pattern IMAGE_REGEX_PATTERN = Pattern.compile(IMAGE_REGEX);

    /**
     * 转换md文件中的图片路径为相对路径
     * @param mdFileAbsolutePath    md文件的绝对路径
     * @param imageAbsolutePath          图片的绝对路径
     * @param secondPath            图片转换为相对路径后, 保存的二级路径
     * @return  图片的相对路径
     * @throws IOException 文件写入异常
     */
    public static String convertToRelativePath(String mdFileAbsolutePath, String imageAbsolutePath, String secondPath) throws IOException {
        if (StrUtil.isBlank(imageAbsolutePath)) {
            throw new IllegalArgumentException("转换路径不能为空!");
        }

        if (StrUtil.isBlank(secondPath)) {
            throw new IllegalArgumentException("二级路径不能为空!");
        }

        if (StrUtil.isBlank(mdFileAbsolutePath)) {
            throw new IllegalArgumentException("md文件名称不能为空!");
        }

        // 如果传入的路径就是相对路径, 就直接返回
        if (imageAbsolutePath.startsWith(".")) {
            return imageAbsolutePath;
        }

        File mdFile = new File(mdFileAbsolutePath);
        if (!mdFile.exists()) {
            throw new RuntimeException(mdFileAbsolutePath + "对应的源文件不存在");
        }
        // 与typora生成规则一致-image下的目录名与md文件名一致且不包含后缀
        String mdFileName = mdFile.getName();
        if (mdFileName.contains(".")) {
            mdFileName = mdFileName.substring(0, mdFileName.lastIndexOf("."));
        }

        File imageFile = new File(imageAbsolutePath);
        if (!imageFile.exists()) {
            throw new RuntimeException(imageAbsolutePath + "对应的源文件不存在");
        }

        File secondPathFile = new File(mdFile.getParent(), secondPath);
        if (!secondPathFile.exists()) {
            boolean flag = secondPathFile.mkdirs();
            if (!flag) {
                throw new RuntimeException("目录" + secondPathFile.getAbsolutePath() + "自动创建失败!");
            }
        }

        File threePathFile = new File(secondPathFile, mdFileName);
        if (!threePathFile.exists()) {
            boolean flag = threePathFile.mkdirs();
            if (!flag) {
                throw new RuntimeException("目录" + threePathFile.getAbsolutePath() + "自动创建失败!");
            }
        }

        File copyImageFile = new File(threePathFile, imageFile.getName());
        String copyFileName = imageFile.getName();
        if (copyImageFile.exists()) {
            // 如果要复制的文件已经存在, 则直接返回
            if (FileUtil.contentEquals(copyImageFile, imageFile)) {
                return "." + File.pathSeparator + secondPath + File.pathSeparator + mdFileName + File.pathSeparator + copyFileName;
            }

            // 如果要复制的文件不存在, 再进行截取文件名
            if (copyFileName.contains(".")) {
                copyFileName = IdUtil.fastSimpleUUID() + copyFileName.substring(copyFileName.indexOf("."));
            } else {
                copyFileName = IdUtil.fastSimpleUUID();
            }
        }

        File realCopyFile = new File(threePathFile, copyFileName);
        try (
                FileInputStream fileInputStream = new FileInputStream(imageAbsolutePath);
                FileOutputStream fileOutputStream = new FileOutputStream(realCopyFile)
        ) {
            IoUtil.copy(fileInputStream, fileOutputStream);
        }

        return "." + File.pathSeparator + secondPath + File.pathSeparator + mdFileName + File.pathSeparator + copyFileName;
    }

    /**
     * 转换md文件中的图片路径为相对路径
     * @param mdFileAbsolutePath    md文件的绝对路径
     * @param imageAbsolutePath          图片的绝对路径
     * @return  图片的相对路径
     * @throws IOException 文件写入异常
     */
    public static String convertToRelativePath(String mdFileAbsolutePath, String imageAbsolutePath) throws IOException {
        return convertToRelativePath(mdFileAbsolutePath, imageAbsolutePath, DEFAULT_SECOND_PATH);
    }

    /**
     * 转换md文件中的绝对路径图片为相对路径并将图片复制到相对路径下
     * @param mdSrc md文件的路径
     */
    public static void convertImagePath(String mdSrc) {
        if (StrUtil.isBlank(mdSrc)) {
            throw new IllegalArgumentException("源文件不能为空!");
        }

        File mdSrcFile = new File(mdSrc);
        if (!mdSrcFile.exists()) {
            throw new IllegalArgumentException("源文件不存在!");
        }
        // 指向不存在的文件不会报错, 所以要先判断文件是否存在
        if (!mdSrcFile.isFile()) {
            throw new IllegalArgumentException("源文件必须是文件类型!");
        }

        // 用于提取md中图片路径的正则
        final String imageRegex = "\\!\\[(?<name>.*?)\\]\\((?<path>.*?)\\)";
        Pattern pattern = Pattern.compile(imageRegex);

        // 读取文件内容
        String mdText = FileUtil.readString(mdSrcFile, StandardCharsets.UTF_8);
        // 保存替换后的结果
        StringBuffer sb = new StringBuffer();
        String replacementText = "";

        Matcher matcherRes = pattern.matcher(mdText);

        while (matcherRes.find()) {
            String mdImageName = matcherRes.group("name");
            String mdImagePath = matcherRes.group("path");

            String imageRelativePath = "";
            try {
                imageRelativePath = MdUtil.convertToRelativePath(mdSrc, mdImagePath);
            } catch (IOException e) {
                System.out.println("(fail)" + mdImagePath);
                continue;
            }
            System.out.println("(success)" + mdImagePath + "->" + imageRelativePath);

            // 构建相对路径的image路径(md格式的)
            replacementText = String.format("![%s](%s)", mdImageName, imageRelativePath);
            // 必须执行replacementText.replace("\\", "\\\\"), 否则替换后的图片路径间缺少\
            // 将替换后的字符串写入sb对象中
            matcherRes.appendReplacement(sb, replacementText.replace("\\", "\\\\"));
        }

        // 将剩余的内容写入sb对象中, 可以百度用法
        matcherRes.appendTail(sb);
        // System.out.println(sb.toString());

        // 将替换后的字符串写入文件
        String movedFileName = mdSrcFile.getName();
        if (mdSrcFile.getName().contains(".")) {
            int index = movedFileName.lastIndexOf(".");
            movedFileName = mdSrcFile.getName().substring(0, index) + "_moved" +  mdSrcFile.getName().substring(index);
        }
        FileUtil.writeString(sb.toString(), new File(mdSrcFile.getParent(), movedFileName), StandardCharsets.UTF_8);
    }

    public static Map<String, List<String>> searchUnusedImage(String mdParentPath, String... imagePaths) {
        Assert.notBlank(mdParentPath);
        File mdPathFile = new File(mdParentPath);
        if (!mdPathFile.exists()) {
            throw new IllegalArgumentException("md源目录不存在!");
        }
        if (!mdPathFile.isDirectory()) {
            throw new IllegalArgumentException("md源目录必须是目录类型的!");
        }

        Assert.notEmpty(imagePaths);
        List<String> imageAbsolutePathList = new ArrayList<>();
        for (int i = 0; i < imagePaths.length; i++) {
            String imageAbsolutePath = null;
            try {
                imageAbsolutePath = MdUtil.convertRelativePathToAbsolutePath(mdPathFile.getAbsolutePath(), imagePaths[i]);
            } catch (IllegalArgumentException e) {
                System.out.println("相对路径" + imagePaths[i] + "转换异常!");
                continue;
            }

            File imageAbsolutePathFile = new File(imageAbsolutePath);
            if (!imageAbsolutePathFile.exists()) {
                System.out.println("相对路径" + imagePaths[i] + "不存在!");
                continue;
            }
            if (!imageAbsolutePathFile.isDirectory()) {
                System.out.println("相对路径" + imagePaths[i] + "非目录类型!");
                continue;
            }

            imageAbsolutePathList.add(imageAbsolutePath);
        }

        // 提取给定目录下的所有文件
        List<String> allImagePaths = new ArrayList<>();
        for (String absoluteImagePathTemp : imageAbsolutePathList) {
            File absoluteImagePathTempFile = new File(absoluteImagePathTemp);
            // 此处遍历可以设置filter, 只查找jpg、gif、png这些类型
            List<File> files = FileUtil.loopFiles(absoluteImagePathTempFile);
            if (CollectionUtil.isNotEmpty(files)) {
                for (File fileTmp : files) {
                    allImagePaths.add(fileTmp.getAbsolutePath());
                }
            }
        }

        List<File> mdFiles = FileUtil.loopFiles(mdPathFile, itemFile -> itemFile.getName().endsWith(".md"));
        if (CollectionUtil.isEmpty(mdFiles)) {
            throw new RuntimeException("源目录" + mdPathFile.getAbsolutePath() + "下没有md文件!");
        }

        Map<String, List<String>> mdFileUseImageMap = new HashMap<>();
        List<String> usedImageList = new ArrayList<>();
        for (File mdFileItem : mdFiles) {
            String mdText = FileUtil.readString(mdFileItem, StandardCharsets.UTF_8);
            Matcher matcherRes = IMAGE_REGEX_PATTERN.matcher(mdText);
            while (matcherRes.find()) {
                String mdImagePath = matcherRes.group("path");
                if (mdImagePath.startsWith("https://") || mdImagePath.startsWith("http://")) {
                    continue;
                }

                String mdImageAbsolutePath = MdUtil.convertRelativePathToAbsolutePath(mdFileItem.getParent(), mdImagePath);
                if (allImagePaths.contains(mdImageAbsolutePath)) {
                    List<String> usedImageListTemp = mdFileUseImageMap.getOrDefault(mdFileItem.getAbsolutePath(), new ArrayList<>());
                    usedImageListTemp.add(mdImageAbsolutePath);
                    mdFileUseImageMap.put(mdFileItem.getAbsolutePath(), usedImageListTemp);
                    usedImageList.add(mdImageAbsolutePath);
                }
            }
        }

        // 计算没有被引用的图片
        List<String> unusedImageList = allImagePaths.stream().filter(item -> !usedImageList.contains(item)).collect(Collectors.toList());
        mdFileUseImageMap.put("unused", unusedImageList);

        Set<Map.Entry<String, List<String>>> entries = mdFileUseImageMap.entrySet();
        for (Map.Entry<String, List<String>> entry : entries) {
            System.out.println("->" + entry.getKey());
            for (String mapImageTmp : entry.getValue()) {
                System.out.println("\t" + mapImageTmp);
            }
        }

        return mdFileUseImageMap;
    }

    /**
     * 转换相对路径为绝对路径
     * @param basePath      pwd的目录
     * @param relativePath  待转换的相对路径
     * @return  转换后的绝对路径
     */
    public static String convertRelativePathToAbsolutePath(String basePath, String relativePath) {
        if (FileUtil.isAbsolutePath(relativePath)) {
            return relativePath;
        }

        String parentPath = basePath;
        File basePathFile = new File(basePath);
        if (FileUtil.isFile(basePath)) {
            parentPath = basePathFile.getParent();
        }

        final String regex = "^.[\\/|\\\\]{1,2}";
        String replaceRelativePath = relativePath.replaceFirst(regex, "");

        if (replaceRelativePath.startsWith("\\") || replaceRelativePath.startsWith("/")) {
            throw new IllegalArgumentException("相对路径" + relativePath + "错误!");
        }

        return new File(parentPath, replaceRelativePath).getAbsolutePath();
    }

    public static String downloadHttpImage(String httpUrl) {
        Assert.notBlank(httpUrl);
        if (!httpUrl.startsWith("https://") || !httpUrl.startsWith("http://")) {
            throw new IllegalArgumentException("下载链接" + httpUrl + "非法!");
        }

        

        return null;
    }

}
